﻿using System;
using System.Xml.Serialization;

namespace Altairis.OtpAuth.Core {
    [XmlRoot(ElementName = "eventOtpVerifier", Namespace = XmlNs.SERIALIZATION_NS_URL)]
    public class EventOtpVerifier : OtpVerifier {

        // Constructors

        public EventOtpVerifier() {
            this.Generator = new EventOtpGenerator();
        }

        // Properties

        private long _counter;

        [XmlAttribute("counter")]
        public long Counter {
            get { return _counter; }
            set {
                if (value < 0) throw new ArgumentOutOfRangeException("Counter cannot be set to negative value.");
                _counter = value;
            }
        }

        [XmlElement("generator")]
        public EventOtpGenerator Generator { get; set; }

        // Methods

        public override bool VerifyOneTimePassword(string otp) {
            if (otp == null) throw new ArgumentNullException("otp");
            if (string.IsNullOrWhiteSpace(otp)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "otp");
            if (this.Generator == null) throw new InvalidOperationException("Cannot generate OTP when Generator property is null.");
            if (this.Secret == null || this.Secret.Length == 0) throw new InvalidOperationException("Cannot generate OTP when Secret property is null or empty.");

            // Verify syntax
            var reqLength = this.Generator.Digits + (this.Generator.UseChecksumDigit ? 1 : 0);
            if (otp.Length < reqLength) otp = otp.PadRight(reqLength);
            if (!this.VerifyPasswordSyntax(otp, this.Generator)) return false;

            // Verify OTP
            var localCounter = this.Counter;
            while (localCounter <= (this.Counter + this.ThrottlingCount)) {
                if (otp.Equals(this.Generator.GenerateOneTimePassword(this.Secret, localCounter))) {
                    // OTP verified - set local counter
                    this.Counter = localCounter + 1;
                    return true;
                }
                localCounter++;
            }
            return false;
        }
    }
}
